﻿#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<ctype.h>
#include<assert.h>

//结构体实现位段
//结构体讲完就得讲讲结构体实现 位段 的能⼒。
//1. 位段的成员必须是 int、unsigned int 或signed int ，在C99中位段成员的类型也可以选择其他类型。
//2. 位段的成员名后边有⼀个冒号和⼀个数字。
//struct A
//{
//	int _a : 2;
//	int _b : 5;
//	int _c : 10;
//	int _d : 30;
//};
//int main()
//{
//	printf("%zd\n", sizeof(struct A));//8
//	return 0;
//}

//位段的内存分配
//1. 位段的成员可以是 int unsigned int signed int 或者是 char 等类型
//2. 位段的空间上是按照需要以4个字节（ int ）或者1个字节（ char ）的⽅式来开辟的。
//3. 位段涉及很多不确定因素，位段是不跨平台的，注重可移植的程序应该避免使⽤位段。


//位段的跨平台问题
//1. int位段被当成有符号数还是⽆符号数是不确定的。
//2. 位段中最⼤位的数⽬不能确定。（16位机器最⼤16，32位机器最⼤32，写成27，在16位机器会出问题。
//3. 位段中的成员在内存中从左向右分配，还是从右向左分配标准尚未定义。
//4. 当⼀个结构包含两个位段，第⼆个位段成员⽐较⼤，⽆法容纳于第⼀个位段剩余的位时，是舍弃
//剩余的位还是利⽤，这是不确定的。
//跟结构相⽐，位段可以达到同样的效果，并且可以很好的节省空间，但是有跨平台的问题存在。

//位段的应⽤
/*下图是⽹络协议中，IP数据报的格式，我们可以看到其中很多的属性只需要⼏个bit位就能描述，这⾥
使⽤位段，能够实现想要的效果，也节省了空间，这样⽹络传输的数据报⼤⼩也会较⼩⼀些，对⽹络
的畅通是有帮助的*/

//位段使⽤的注意事项
//位段的⼏个成员共有同⼀个字节，这样有些成员的起始位置并不是某个字节的起始位置，那么这些位
//置处是没有地址的。内存中每个字节分配⼀个地址，⼀个字节内部的bit位是没有地址的。
//所以不能对位段的成员使⽤& 操作符，这样就不能使⽤scanf直接给位段的成员输⼊值，只能是先输⼊
//放在⼀个变量中，然后赋值给位段的成员



//struct A
//{
//	int _a : 2;
//	int _b : 5;
//	int _c : 10;
//	int _d : 30;
//};
//
//int main()
//{
//	struct A sa = { 0 };
//	scanf("%d", &sa._b);//这是错误的 
//
//	//正确的⽰范 
//	int b = 0;
//	scanf("%d", &b);
//	sa._b = b;
//	return 0;
//}


//自定义类型
//1.结构体
//2.联合体
//3.枚举

//联合体
//联合体类型的声明
//像结构体⼀样，联合体也是由⼀个或者多个成员构成，这些成员可以不同的类型。
//但是编译器只为最⼤的成员分配⾜够的内存空间。联合体的特点是所有成员共⽤同⼀块内存空间。所以联合体也叫：共⽤体。
//给联合体其中⼀个成员赋值，其他成员的值也跟着变化

//联合类型的声明 
//union Un
//{
//	char c;
//	int i;
//};
//int main()
//{
//	//联合变量的定义 
//	union Un un = { 0 };
//	//计算连个变量的⼤⼩
//	printf("%zd\n", sizeof(un));//4
//	return 0;
//}


//联合体的特点
//联合的成员是共⽤同⼀块内存空间的，这样⼀个联合变量的⼤⼩，⾄少是最⼤成员的⼤⼩（因为联合⾄少得有能⼒保存最⼤的那个成员）。

//联合类型的声明 
//union Un
//{
//	char c;
//	int i;
//};
//int main()
//{
//	//联合变量的定义 
//	union Un un = { 0 };
//	// 下⾯输出的结果是⼀样的吗？ 
//	printf("%p\n", &(un.i));
//	printf("%p\n", &(un.c));
//	printf("%p\n", &un);
//	return 0;
//}


//联合类型的声明 
//union Un
//{
//	char c;
//	int i;
//};
//int main()
//{
//	//联合变量的定义 
//	union Un un = { 0 };
//	un.i = 0x11223344;
//	un.c = 0x55;
//	printf("%x\n", un.i);
//	return 0;
//}

//联合体⼤⼩的计算
//• 联合的⼤⼩⾄少是最⼤成员的⼤⼩
//• 当最⼤成员⼤⼩不是最⼤对⻬数的整数倍的时候，就要对⻬到最⼤对⻬数的整数倍。

//union Un1
//{
//	char c[5];
//	int i;
//};
//union Un2
//{
//	short c[7];
//	int i;
//};
//int main()
//{
//	//下⾯输出的结果是什么？ 
//	printf("%zd\n", sizeof(union Un1));//8
//	printf("%zd\n", sizeof(union Un2));//16
//	return 0;
//}

//使⽤联合体是可以节省空间的,举例:
//⽐如，我们要搞⼀个活动，要上线⼀个礼品兑换单，礼品兑换单中有三种商品：图书、杯⼦、衬衫。
//每⼀种商品都有：库存量、价格、商品类型和商品类型相关的其他信息。
//图书：书名、作者、⻚数
//杯⼦：设计
//衬衫：设计、可选颜⾊、可选尺⼨
//那我们不耐⼼思考，直接写出⼀下结构：

//struct gift_list
//{
//	//公共属性 
//	int stock_number;//库存量 
//	double price; //定价 
//	int item_type;//商品类型 
//
//	//特殊属性 
//	char title[20];//书名
//	char author[20];//作者 
//	int num_pages;//⻚数 
//
//	char design[30];//设计 
//	int colors;//颜⾊ 
//	int sizes;//尺⼨ 
//};

//上述的结构其实设计的很简单，⽤起来也⽅便，但是结构的设计中包含了所有礼品的各种属性，这样
//使得结构体的⼤⼩就会偏⼤，⽐较浪费内存。因为对于礼品兑换单中的商品来说，只有部分属性信息
//是常⽤的。⽐如：
//商品是图书，就不需要design、colors、sizes。
//所以我们就可以把公共属性单独写出来，剩余属于各种商品本⾝的属性使⽤联合体起来，这样就可以
//介绍所需的内存空间，⼀定程度上节省了内存。

//struct gift_list
//{
//	int stock_number;//库存量 
//	double price; //定价 
//	int item_type;//商品类型 
//
//	union {
//		struct
//		{
//			char title[20];//书名 
//			char author[20];//作者 
//			int num_pages;//⻚数 
//		}book;
//		struct
//		{
//			char design[30];//设计 
//		}mug;
//		struct
//		{
//			char design[30];//设计 
//			int colors;//颜⾊ 
//			int sizes;//尺⼨ 
//		}shirt;
//	}item;
//};

//写⼀个程序，判断当前机器是⼤端？还是⼩端？

//int check_sys()
//{
//	union
//	{
//		int i;
//		char c;
//	}un;
//	un.i = 1;
//	return un.c;//返回1是⼩端，返回0是⼤端 
//}













